/************************************************************
 * $Id : skylistener.cpp   2011/08/01
 *
 *       swhors@naver.com
 ************************************************************/

#include "config.h"

#include <errno.h>
#include <string>

#include <iostream>
#include <vector>
#include <algorithm>

#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>

#include <stdarg.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <fcntl.h>

#include <error.h>

using namespace std;

#include "constant.h"

#include "skylistener.h"
#include "ast_util.h"
#include "peg_def.h"
#include "ast_ami.h"
#include "ast_main.h"
#include "SkyConf.h"

void Signal_Handler( int sig )
{
    log_message( (char*)"DEBUG : %s %s [sig val : %d]\n",
            (char*)PROG_NAME,
            (char*)("received signal..."),
            sig);

    switch( sig )
    {
        case SIGHUP:
            /* rehash the server */
            cout << PROG_NAME << " received SIGHUP..." << endl;
            log_message((char*) "INFO : %s %s\n",(char*)PROG_NAME, (char*)"received SIGHUP..." );
            stop_ami_server();
            break;
        case SIGTERM:
            /* finalize the server */
            cout << PROG_NAME << " received SIGTERM..." << endl;
            log_message((char*)"INFO : %s %s\n",(char*)PROG_NAME,(char*)"received SIGTERM..." );
            stop_ami_server();
            exit(0);
            break;
        case SIGKILL:
            /* finalize the server */
            cout << PROG_NAME << " received SIGKILL..." << endl;
            log_message((char*)"INFO : %s %s\n",(char*)PROG_NAME,(char*)"received SIGKILL..." );
            stop_ami_server();
            exit(0);
            break;
        default:
            break;
    }
}

void SignalSet()
{
    signal(SIGQUIT,SIG_IGN); /* ignore*/
    signal(SIGABRT,SIG_IGN); /* ignore*/
    signal(SIGSYS,SIG_IGN); /* ignore*/
    signal(SIGPIPE,SIG_IGN); /* ignore*/
    signal(SIGALRM,SIG_IGN); /* ignore*/
    signal(SIGCHLD,SIG_IGN); /* ignore child */
    signal(SIGTSTP,SIG_IGN); /* ignore tty signals */
    signal(SIGTTOU,SIG_IGN);
    signal(SIGTTIN,SIG_IGN);
    signal(SIGSTOP,Signal_Handler);
    signal(SIGKILL,Signal_Handler); /* kill signal */
    signal(SIGHUP, Signal_Handler); /* hangup signal */
    signal(SIGTERM,Signal_Handler); /* Software termination signal from kill */
}

void print_usage( char *prog_name )
{
    cout << endl;
    cout << "=============================================" << endl;
    cout << "Usage : " << prog_name  << " [start mode]" << endl;
    cout << "---------------------------------------------" << endl;
    cout << "        " << "start mod : "<< endl;
    cout << "        " << "0 --> start "<< endl;
    cout << "        " << "1 --> restart "<< endl;
    cout << "        " << "2 --> process kill"<< endl;
    cout << "=============================================" << endl;
    cout << endl;
}

int get_pid_by_name( char *p_name, char *opt )
{
    FILE  *pstr     = NULL;
    char  *p        = NULL;
    char  cmd[128]  = {0,};
    char  buff[512] = {0,};

    memset( cmd, 0, 128);
    snprintf( cmd, 128, "ps -ef | grep \"%s %s\"", p_name, opt);

    pstr = popen( cmd, "r" );
    if( pstr == NULL )
    {
        return 0;
    }
    else
    {
        /* do nothing */
    }

    memset( buff, 0, 512 );
    p = fgets( buff, 512, pstr );

    p = strtok( buff, "    " );
    p = strtok( NULL, "    " );

    pclose( pstr );
    if( p != NULL )
    {
        if( strlen(p) != 0 )
        {
            return atoi( p );
        }
        else
        {
            /* do nothing */
        }
    }
    else
    {
        /* do nothing */
    }

    return 0;
}

int kill_process()
{
    int  pid         = 0;
    int  sLockFileFd = 0;
    char s_pid[10]   = {0,};

    sLockFileFd = open( LOCK_FILE, O_RDONLY );

    if( sLockFileFd > 0 )
    {
        /* first instance continues */
        if( read( sLockFileFd, s_pid, 10) > 0 )
        {
            pid = atoi( s_pid );
        }
        else
        {
            /* do nothing */
        }
        close( sLockFileFd );
    }
    else
    {
        /* do nothing */
    }

    if( pid == 0 )
    {
        pid = get_pid_by_name( (char*)PROG_NAME, (char*)"0" );
        if( pid == 0 )
        {
            pid = get_pid_by_name( (char*)PROG_NAME, (char*)"1" );
        }
        else
        {
            /* do nothing */
        }
    }
    else
    {
        /* do nothing */
    }

    if( pid > 0 )
    {
        log_message( (char*)"INFO : Process Kill [%d]\n", pid );
        killpg( pid, SIGTERM );
    }
    else
    {
        /* do nothing */
    }
    remove( LOCK_FILE );

    return 1;
}

void daemonize()
{
    pid_t  sID              = 0;
    int    sNullDevFD       = 0;
    int    sSize            = 0;
    int    sLockFileFd      = 0;
    char   sStrWriteBuf[10] = {0,};
    int    sRet             = 0;
    size_t sRetSize         = 0;
    size_t sWriteSize       = 0;

    printf( "%s %s %d\n", __FILE__, __FUNCTION__, __LINE__ );

    if( getppid() == 1 )
    {
        return; /* already a daemon */
    }
    printf( "%s %s %d\n", __FILE__, __FUNCTION__, __LINE__ );

    sID = fork();

    if( sID < 0 )
    {
        printf( "%s %s %d\n", __FILE__, __FUNCTION__, __LINE__ );
        exit( 1 ); /* fork error */
    }

    printf( "%s %s %d\n", __FILE__, __FUNCTION__, __LINE__ );

    if( sID > 0 )
    {
        printf( "fork : %d\n", sID );
        exit( 0 ); /* parent exits */
    }

    printf( "%s %s %d\n", __FILE__, __FUNCTION__, __LINE__ );

    /* child (daemon) continues */
    setsid(); /* obtain a new process group */
    printf( "%s %s %d\n", __FILE__, __FUNCTION__, __LINE__ );
    for ( sSize = getdtablesize() ; sSize > 1 ; --sSize )
    {
        /* close all descriptors */
        printf( "%s %s %d\n", __FILE__, __FUNCTION__, __LINE__ );
        close( sSize );
        printf( "%s %s %d\n", __FILE__, __FUNCTION__, __LINE__ );
    }

    printf( "%s %s %d\n", __FILE__, __FUNCTION__, __LINE__ );

    // handle standart I/O
    sNullDevFD = open( "/dev/null", O_RDWR);
    if( sNullDevFD == -1 )
    {
        printf( "Couldn't open file %s; %s\n",
                LOCK_FILE,
                strerror( errno ) );
        exit(1);
    }
    sRet = dup( sNullDevFD );
    sRet = dup( sNullDevFD ); /* handle standart I/O */

    umask( 027 ); /* set newly created file permissions */
    //chdir(RUNNING_DIR); /* change running directory */

    printf( "%s %s %d\n", __FILE__, __FUNCTION__, __LINE__ );
    sLockFileFd = open( LOCK_FILE, O_RDWR|O_CREAT, 0640);
    if( sLockFileFd < 0 )
    {
        printf( "Couldn't open file %s; %s\n",
                LOCK_FILE,
                strerror( errno ) );
        exit( 1 ); /* can not open */
    }
    else
    {
        /* do nothing */
    }
    printf( "%s %s %d\n", __FILE__, __FUNCTION__, __LINE__ );

    if( lockf( sLockFileFd, F_TLOCK, 0) < 0 )
    {
        fprintf( stderr,
                "Couldn't open file %s; %s\n",
                LOCK_FILE,
                strerror( errno ) );
        exit( 0 ); /* can not lock */
    }
    else
    {
        /* do nothing */
    }
    printf( "%s %s %d\n", __FILE__, __FUNCTION__, __LINE__ );

    /* first instance continues */
    sprintf( sStrWriteBuf, "%d\n", getpid() );

    sWriteSize = (size_t)strlen( sStrWriteBuf );
    printf( "%s %s %d\n", __FILE__, __FUNCTION__, __LINE__ );

    sRetSize = write( sLockFileFd,
                      sStrWriteBuf,
                      (size_t)sWriteSize ); /*record pid to lockfile*/
    if( sRetSize == sWriteSize )
    {
        /* success to write */
    }
    else
    {
        /* fail to write */
    }
    printf( "%s %s %d\n", __FILE__, __FUNCTION__, __LINE__ );

    close( sLockFileFd );
    SignalSet();
    printf( "%s %s %d\n", __FILE__, __FUNCTION__, __LINE__ );

}

int main(int argc, char **argv)
{
    int sStartMode = 0;

    if( argc == 2 )
    {
        sStartMode = atoi(argv[1]);

        switch( sStartMode )
        {
            case 3:
                //un-daemonize
                log_message( (char*) "INFO : %s %s\n",
                        (char*)PROG_NAME,
                        (char*)"startted...." );
                run_ami_server(1);
                break;
            case 0:
                // new start
                log_message( (char*) "INFO : %s %s\n",
                        (char*)PROG_NAME,
                        (char*)"startted...." );
                daemonize();
                run_ami_server(1);
                break;
            case 1:
                // re-start
                log_message( (char*)"INFO : %s %s\n",
                        (char*)PROG_NAME,
                        (char*)"restartted...." );
                kill_process();
                Sleep(4000);
                daemonize();
                run_ami_server(1);
                break;
            case 2:
                // kill process;
                log_message( (char*)"INFO : %s %s\n",
                        (char*)PROG_NAME,
                        (char*)"requested to kill...." );
                kill_process();
                return 0;
                break;
            default:
                print_usage( argv[0] );
                break;
        }
    }
    else
    {
        print_usage( argv[0] );
    }
    return 0;
}
